Skip to content

feat(codex): add encrypted native main profiles - #863

Merged
Wibias merged 67 commits into
lidge-jun:devfrom
luvs01:feat/656-native-main-profiles
Aug 3, 2026
Merged

feat(codex): add encrypted native main profiles#863
Wibias merged 67 commits into
lidge-jun:devfrom
luvs01:feat/656-native-main-profiles

Conversation

@luvs01

@luvs01 luvs01 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Add an opt-in, CLI/backend-only native-main profile workflow under ocx account main: doctor, list, register, add, switch, and recover. It changes the physical Codex login in the effective CODEX_HOME, is independent from Pool routing, and preserves task/history files.

Each native Codex auth.json is handled as an exact opaque envelope. Inactive profiles and the recovery journal use AES-256-GCM with AAD binding and a random master key held by the native OS credential store through @napi-rs/keyring. The feature supports Codex credential-store mode file only and fails closed for unavailable keyring, unsupported keyring/auto/ephemeral modes, invalid layouts, and home/path mismatches. There is no plaintext key-file or shell fallback.

Safety and recovery

  • Switching is scoped to one effective Codex home and serialized through the cross-process lock.
  • The switch drains OpenCodex __main__ work, requires native Codex to be stopped, atomically publishes the exact target bytes, verifies read-back, updates encrypted vault/runtime state, and restores the exact source on failure.
  • A startup recovery gate blocks only __main__ traffic while an unresolved journal exists; health, management, Direct, and ordinary Pool traffic remain available.
  • Explicit recovery converges every durable phase. Rollback preserves a refreshed target envelope in its encrypted inactive record before restoring source auth.
  • Pending recovery blocks register, staging creation, and stage completion under the same home lock with actionable RECOVERY_REQUIRED; list, doctor, cancel, recover, and switch recovery remain available.
  • Staged codex login runs in an isolated restricted home. Plaintext staging credentials are removed on success, failure, cancellation, and expiry.
  • Native Codex is never terminated. A successful switch requires restarting it.
  • Only __main__ runtime-derived state is reconciled; tasks and history remain untouched.

Management API boundary

The native-profile management routes are behind the existing local management authentication, CSRF, and origin gates. They carry labels, profile IDs, and staging IDs only; they do not accept or return auth envelopes, access tokens, refresh tokens, raw account IDs, or decrypted vault payloads. Real-server route-admission tests cover missing/wrong admin auth, hostile origins, GUI session/CSRF rejection, and valid admin or GUI admission.

Maintainer follow-up addressed

  • Server-authoritative plaintext staging cleanup and expiry handling.
  • Cross-process lock reclamation race removal and crash/successor coverage.
  • Startup journal gating and recovery convergence before main-account traffic.
  • Process-crash coverage at durable boundaries plus concurrency, drain, stale-response, rollback, and WebSocket invalidation cases.
  • Blocking bun audit --audit-level=high release/CI gates and supported-OS keyring create/read/delete smoke jobs.
  • Real management-route auth, CSRF, and origin tests.
  • Latest-dev rebase, corrected CLI documentation, pending-recovery mutation guard, and refreshed-target rollback preservation.

Validation

  • Native-profile, route-admission, startup, WebSocket, CLI, and crash suite: 59 passed, 0 failed, 442 assertions.
  • Keyring unit smoke: 2 passed, 0 failed.
  • Live Windows OS-keyring create/read/delete smoke: passed.
  • Typecheck: passed.
  • Privacy scan: passed.
  • bun audit --audit-level=high: passed.
  • GUI lint: passed.
  • git diff --check upstream/dev...HEAD: passed.
  • Standard review of the final recovery-state fix: no remaining findings.

A broad local root-suite run completed 6,918 passing and 6 skipped tests, with five fixed 5-second timeout overruns in unchanged account-store/auth-context/routing tests. The run took about 1,555 seconds versus the suite's roughly 210-second normal-duration warning, and isolated failures exposed the existing Windows ACL/timing environment rather than native-profile assertions. Hosted CI on this exact head is the authoritative cross-platform result.

The docs-site frozen install currently reports that the existing upstream bun.lock would change before a docs build can start. This PR does not rewrite that unrelated lockfile state.

Scope

No dashboard UI, Pool-to-native credential conversion, native process termination, non-file credential-store support, or plaintext fallback is included.

Fixes #656
Related context: #821, #823

Summary by CodeRabbit

  • New Features
    • Added native Codex login profile management through ocx account main, including listing, registration, switching, diagnostics, and recovery.
    • Added profile management API support with confirmation, request draining, and structured error responses.
    • Added encrypted profile storage with crash-safe switching and recovery safeguards.
  • Bug Fixes
    • Improved account transitions and quota reporting to prevent stale account data.
  • Documentation
    • Documented native Codex profile commands and recovery workflows.
  • Security
    • Added dependency auditing and cross-platform keyring health checks to release validation.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b2dfd491-20b0-4776-83c0-0eb7e3df8424

📥 Commits

Reviewing files that changed from the base of the PR and between 42d63ef and 2736ae1.

📒 Files selected for processing (3)
  • src/providers/quota.ts
  • tests/keyring-smoke.test.ts
  • tests/provider-quota.test.ts

📝 Walkthrough

Walkthrough

Native Codex main profiles now support encrypted storage, staged login, switching, recovery, startup fencing, CLI and management APIs, quota identity fencing, and extensive validation. CI and release workflows add high-severity dependency audits and cross-platform keyring smoke tests.

Changes

Native Codex profile management

Layer / File(s) Summary
Profile contracts and encrypted storage
src/codex/native-profile-types.ts, src/codex/native-profile-store.ts, devlog/_plan/260801_native_main_profiles/*, docs-site/src/content/docs/*/reference/cli/providers-accounts.md
Defines profile contracts, canonical CODEX_HOME handling, file-credential validation, OS-keyring keys, AES-256-GCM encryption, and vault and journal validation.
Profile lifecycle and recovery
src/codex/native-profile-manager.ts, src/codex/native-profile-processes.ts, src/codex/native-profile-recovery.ts, src/codex/native-profile-startup.ts
Adds locked registration, staging, switching, rollback, crash recovery, process checks, atomic persistence, startup gating, and secure cleanup.
Server, account, and quota integration
src/codex/native-profile-api.ts, src/server/*, src/codex/auth-api.ts, src/codex/account-lifecycle.ts, src/codex/account-usability.ts, src/codex/auth-context.ts, src/providers/quota.ts
Adds management routes, request draining, native-main claims, account fencing, runtime cleanup, and generation-aware quota publication.
CLI profile operations
src/cli/account-main.ts, src/cli/account.ts, src/cli/account-api.ts, src/cli/help.ts
Adds diagnostics, listing, registration, staged login, switching, recovery, confirmations, output modes, and injected login execution.
Profile validation
tests/native-profile-*.test.ts, tests/cli-native-profile.test.ts, tests/helpers/native-profile-*, tests/provider-quota.test.ts, tests/codex-websocket-registry.test.ts
Tests lifecycle behavior, route security, locking, staging, crash recovery, concurrency, startup gating, Pool isolation, CLI cleanup, runtime transitions, and stale quota rejection.

Audit and keyring safeguards

Layer / File(s) Summary
Dependency audit and keyring verification
.github/workflows/*.yml, package.json, gui/package.json, scripts/keyring-smoke.ts, scripts/release.ts, tests/keyring-smoke.test.ts, tests/ci-workflows.test.ts, tests/release-helper.test.ts
Adds high-severity dependency audits, keyring dependency overrides, an injectable create/read/delete smoke test, release preflight checks, and isolated cross-platform keyring workflow execution.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant ManagementAPI
  participant NativeProfileManager
  participant CodexAuth
  participant StartupGate

  CLI->>ManagementAPI: register, switch, or recover profile
  ManagementAPI->>NativeProfileManager: validate and execute operation
  NativeProfileManager->>CodexAuth: replace and verify native auth envelope
  NativeProfileManager->>StartupGate: publish transition or complete recovery
  StartupGate->>CodexAuth: allow native main traffic after recovery
Loading

Suggested reviewers: ingwannu, lidge-jun, wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the PR's primary change: encrypted native Codex main profiles.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 1, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

Maintainer security review on head 9a2c1fa4. We want this capability (#656) and intend to drive it to landing — this review is the merge bar, not a rejection of the design. The core encryption design is sound: random 32-byte master key in the OS keyring, AES-256-GCM with AAD binding, fail-closed on keyring errors, no credential material in logs or API payloads, and the management endpoints inherit auth + CSRF + origin enforcement. The @napi-rs/keyring@1.3.0 dependency checks out on publication properties (prebuilt N-API binaries, no install scripts, locked integrity, npm provenance).

Must change before merge (priority order):

  1. Residual plaintext staging credentials. A failed/aborted enrollment can leave a plaintext token envelope indefinitely: non-200 /stage/finish returns early bypassing cleanup (src/cli/account-main.ts:96-109); finishStage() removes the stage only on success; after 30 minutes verifiedStagePath() rejects the stage while cancelStage() requires that same verification, making cleanup impossible (native-profile-manager.ts:307-320,406-408). Every failure path must clean the stage server-side, with stale-stage cleanup that works after expiry. Add the failure tests.
  2. Lock-reclamation race. Malformed lock contents are treated as immediately stale and unlinked (native-profile-manager.ts:124-160) — a process can observe the lock between creation and metadata write, or reclamation can delete a new live lock, allowing two concurrent credential switches. Use an OS-backed lock or ownership-nonce compare-and-delete protocol with cross-process race tests.
  3. Journal gating on startup. A crash between target auth.json publication and vault commit leaves login B active while vault metadata names A; nothing detects this on startup, so the proxy can serve B before ownership is finalized. Converge recoverable journals on startup or fail closed for main-account traffic until explicit recovery.
  4. Process-crash integration tests at every durable boundary (journal prepared, auth replaced, vault committed, runtime transition, journal deleted), plus the negative concurrency/runtime matrix your design doc already lists (drain timeout, stale-response fencing, process-busy, vault-write failure after auth replacement, rollback verification failure, two concurrent switches).
  5. Release evidence for the native dependency: add bun audit --audit-level=high to CI/release and an ephemeral OS-keyring create/read/delete smoke per supported OS.
  6. Route security tests for the seven new management operations proving rejection without admin credentials / valid CSRF / proper origin — the current API test bypasses the real gate by calling the inner handler. Also correct the PR body: it says it adds no management route, but it registers credential-writer routes.
  7. Rebase onto current dev (the branch now conflicts), rerun the full matrix, and request explicit security review.

Items 1-3 are the true blockers. If you are short on cycles, say so — a maintainer will take over on top of your branch. This is genuinely close to the capability we want to ship.

@luvs01
luvs01 force-pushed the feat/656-native-main-profiles branch from 9a2c1fa to fc1f6ab Compare August 2, 2026 03:35
@luvs01
luvs01 marked this pull request as ready for review August 2, 2026 03:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fc1f6ab7a7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/account-main.ts Outdated
Comment thread src/codex/native-profile-api.ts Outdated
Comment thread src/codex/native-profile-manager.ts Outdated
Comment thread src/codex/native-profile-manager.ts
Comment thread src/codex/native-profile-startup.ts Outdated
Comment thread src/codex/native-profile-store.ts Outdated
Comment thread src/codex/native-profile-api.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 218-236: The non-Linux branch of the “OS keyring
create/read/delete smoke” workflow runs directly against persistent self-hosted
keyring state. Update this step to isolate the self-hosted Windows leg from the
real Credential Manager, or add reliable pre- and post-job cleanup that removes
all lingering opencodex.keyring-smoke.* entries, including when the smoke test
is interrupted.

In `@docs-site/src/content/docs/reference/cli/providers-accounts.md`:
- Around line 213-232: Synchronize the `ocx account main` documentation across
the English, Japanese, Korean, Russian, and Simplified Chinese
`providers-accounts.md` pages. Update the English command block to use `<label>`
for profile creation, `<profile-id-or-label>` for `switch`, retain `switch
--yes`, and document every supported `--json` flag according to
`src/cli/account-main.ts` and its `resolveTarget` behavior; add the
corresponding section to the three non-English locale pages.

In `@scripts/keyring-smoke.ts`:
- Around line 34-51: The nested cleanup in runKeyringSmoke must not mask a
pending readback-mismatch error: in scripts/keyring-smoke.ts lines 34-51, log
deleteCredential failure instead of re-throwing it. Add a regression case in
tests/keyring-smoke.test.ts lines 44-56 where getSecret mismatches and
deleteCredential fails, asserting runKeyringSmoke rejects with the original
“readback did not match” message.

In `@src/codex/account-lifecycle.ts`:
- Around line 43-60: Extract the repeated main-account cleanup sequence from
reconcileMainCodexAccountRuntimeState and
applyConfirmedMainCodexAccountTransition into a shared helper. Have that helper
call purgeCodexAccountRuntimeState, setMainAccountPlan(null), and
invalidateCodexWebSocketsForAccount for MAIN_CODEX_ACCOUNT_ID, then replace both
inline sequences with the helper while preserving transition behavior.

In `@src/codex/native-profile-api.ts`:
- Around line 101-109: Update the generic fallback branch in the native-profile
error handler to return a distinct internal-failure code such as
"INTERNAL_ERROR" instead of "RECOVERY_REQUIRED". Preserve "RECOVERY_REQUIRED"
exclusively for the NativeProfileError path emitted by assertNoPendingRecovery.

In `@src/codex/native-profile-manager.ts`:
- Around line 494-507: Refactor the operation around the existing native profile
import body and cleanup so deleteStageById never throws from finally. Always
perform buffer zeroization and staging cleanup, capture cleanup failure,
preserve and rethrow any original operation error, and only raise
STAGING_CLEANUP_REQUIRED after a successful writeVault result. Update that error
message to state the profile was imported before cleanup failed, then return the
committed profile normally when cleanup succeeds.
- Around line 310-344: Update doctor() to catch failures from sweepStaleStages()
and readNativeProfileVault(), recording degraded classifications in new
vaultStatus and stagingSweep fields while preserving the existing diagnostic
report. Ensure corrupt vaults still return profileCount and activeProfileId
safely, and staging cleanup failures do not abort the report; keep
credential-store, auth, and recoveryPending diagnostics available.

In `@src/codex/native-profile-processes.ts`:
- Line 10: Validate the SystemRoot-derived executable path before the PowerShell
invocation in the native profile process flow, using the existing C:\Windows
fallback whenever the environment value is absent or implausible. Ensure
execFileSync uses only the validated Windows system path and does not execute a
location selected from an untrusted writable directory.
- Around line 17-43: Set an explicit sufficiently large maxBuffer on both
execFileSync calls in powershellProcessCount and unixProcessCount, including the
ps output path with full arguments. Preserve the existing counting and
validation behavior while preventing expected process-list output from raising
ENOBUFS and being downgraded by probeNativeCodexProcesses to unknown.

In `@src/codex/native-profile-recovery.ts`:
- Around line 78-91: Update decideNativeProfileRecovery to consume
observation.digest and set an externallyRefreshed signal when the target has a
changed digest during the commit-recovery path, while preserving the existing
commit-target action. Add that signal to NativeProfileRecoveryDecision and
propagate it through recoverLocked’s returned object so CLI and management API
callers can report the external refresh; otherwise remove the unused digest
field if warning behavior is intentionally deferred.

In `@src/codex/native-profile-store.ts`:
- Around line 358-375: Separate malformed or unreadable journals from valid
pending journals in readNativeProfileJournal, while preserving RECOVERY_REQUIRED
for normal fail-closed credential operations. Update recover/recoverLocked so an
explicit confirmed rollback catches the malformed-journal case, renames
context.journalPath to a unique quarantine filename using the transaction ID or
timestamp, leaves auth.json and the vault unchanged, and returns the quarantine
path. Keep register, switching, and non-confirmed recovery blocked.
- Around line 381-387: Update validateNativeProfileLabel and parseVaultObject to
share a predicate that rejects bidi override/isolate characters and zero-width
characters in addition to the existing control-character checks. Define the
predicate once near the profile-label validation logic, reuse it in both
validation paths, and preserve trimming, length limits, and the existing
INVALID_REQUEST error behavior.

In `@src/codex/native-profile-types.ts`:
- Line 68: Remove the duplicate NativeProfileJournalPhase declaration from
native-profile-recovery.ts and import/re-export the canonical type from
native-profile-types.ts. Keep consumers such as decideNativeProfileRecovery
using the shared type so native-profile-store.ts and recovery logic remain
synchronized.

In `@tests/helpers/native-profile-lock-child.ts`:
- Around line 19-23: Change the simulated crash branch in onLockAcquired to exit
with the distinct non-zero code used by native-profile-switch-child.ts, then
update the corresponding assertion in native-profile-manager.test.ts to expect
that code so the test verifies the crash path executed.

In `@tests/helpers/native-profile-startup-child.ts`:
- Around line 71-77: Update the promise chain around
waitForNativeMainStartupGate() to attach a rejection handler that writes the
failure details into settledPath, including errors thrown by
nativeMainStartupGateSnapshot(), isMainAccountTokenLive(), or loadConfig() in
the success callback. Preserve the existing success payload and ensure every
failure path writes a JSON record so parent tests surface the cause instead of
timing out.

In `@tests/native-profile-api.test.ts`:
- Around line 51-79: Update the test “stale HTTP/Responses-WebSocket work
settles before switch and new turns stay fenced” to release both admission
leases with try/finally cleanup: always release oldTurn after the request flow,
and always release after when it is acquired. Preserve the existing assertions
and ordering checks while ensuring cleanup runs if the drain returns early or
any assertion throws.
- Line 114: Update the username-leak assertion in the native-profile API test to
read the current OS account name from a cross-platform source that resolves on
Windows, Linux, and macOS, rather than relying only on process.env.USERNAME.
Keep the assertion focused on ensuring the redacted management error payload
does not contain that resolved identity.

In `@tests/native-profile-crash-boundaries.test.ts`:
- Around line 17-21: Restore captured environment variables through a shared
helper that deletes the key when the captured value is undefined, otherwise
assigns the saved value. Apply this in
tests/native-profile-crash-boundaries.test.ts at lines 17-21 and 70-71, and in
tests/native-profile-startup.test.ts at lines 34-38 and 171-172, covering the
afterEach hooks and mid-fixture restores.
- Around line 196-216: Wrap the concurrent-switch test body in a finally block
that always writes firstRelease and awaits both first.exited and second.exited,
including when waitFor or an assertion fails. Mirror the cleanup pattern used by
the nearby test and the existing native-profile-manager test, while preserving
the current success assertions.

In `@tests/native-profile-manager.test.ts`:
- Around line 85-87: Update the journal-phase checks in the test’s injected
failure logic at both matching locations to parse content with JSON.parse and
compare its phase property to "auth-replaced". Replace formatting-dependent
substring matching while preserving the existing path condition and failure
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7593e26e-ba06-4588-acb8-b66bf30f340a

📥 Commits

Reviewing files that changed from the base of the PR and between 253455b and fc1f6ab.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (40)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • devlog/_plan/260801_native_main_profiles/000_design.md
  • devlog/_plan/260801_native_main_profiles/001_validation.md
  • devlog/_plan/260801_native_main_profiles/002_implementation.md
  • docs-site/src/content/docs/reference/cli/providers-accounts.md
  • package.json
  • scripts/keyring-smoke.ts
  • scripts/release.ts
  • src/cli/account-api.ts
  • src/cli/account-main.ts
  • src/cli/account.ts
  • src/cli/help.ts
  • src/codex/account-lifecycle.ts
  • src/codex/account-usability.ts
  • src/codex/auth-api.ts
  • src/codex/auth-context.ts
  • src/codex/native-profile-api.ts
  • src/codex/native-profile-manager.ts
  • src/codex/native-profile-processes.ts
  • src/codex/native-profile-recovery.ts
  • src/codex/native-profile-startup.ts
  • src/codex/native-profile-store.ts
  • src/codex/native-profile-types.ts
  • src/server/index.ts
  • src/server/lifecycle.ts
  • src/server/management-api.ts
  • src/server/management/context.ts
  • tests/cli-native-profile.test.ts
  • tests/codex-websocket-registry.test.ts
  • tests/helpers/native-profile-lock-child.ts
  • tests/helpers/native-profile-startup-child.ts
  • tests/helpers/native-profile-switch-child.ts
  • tests/keyring-smoke.test.ts
  • tests/native-profile-api.test.ts
  • tests/native-profile-crash-boundaries.test.ts
  • tests/native-profile-manager.test.ts
  • tests/native-profile-recovery.test.ts
  • tests/native-profile-route-security.test.ts
  • tests/native-profile-startup.test.ts

Comment thread .github/workflows/ci.yml
Comment thread docs-site/src/content/docs/reference/cli/providers-accounts.md
Comment thread scripts/keyring-smoke.ts
Comment thread src/codex/account-lifecycle.ts
Comment thread src/codex/native-profile-api.ts
Comment thread tests/native-profile-api.test.ts
Comment thread tests/native-profile-api.test.ts Outdated
Comment thread tests/native-profile-crash-boundaries.test.ts
Comment thread tests/native-profile-crash-boundaries.test.ts
Comment thread tests/native-profile-manager.test.ts Outdated
@Wibias
Wibias marked this pull request as draft August 2, 2026 21:07
@luvs01
luvs01 force-pushed the feat/656-native-main-profiles branch from fc1f6ab to 8790758 Compare August 3, 2026 06:01
@luvs01

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Draft checkpoint update

Pushed 87907584 on top of current dev fa51fce5 so the public branch now reflects the completed local integration instead of the obsolete pre-review head.

This checkpoint includes the implementation for all 27 existing review threads plus the follow-up hardening completed during integration: canonical CODEX_HOME process ownership and shared claims, startup/recovery admission fences, leased staging writers and cleanup, crashed auth-write residue handling, effective-home/runtime resolution, bounded asynchronous process probing, account metadata generation fences, and current sharded CI/keyring-smoke integration. The keyring-smoke checkout retains persist-credentials: false.

Local validation on Bun 1.4.0-canary.1:

  • TypeScript typecheck: pass
  • privacy scan: pass
  • git diff --check: pass
  • standard isolated focused matrix: all unchanged cases pass
  • Claude endpoint after the final test-harness reconciliation: 27/27
  • native-profile drain server: 3/3
  • CI workflow assertions: 66/66
  • final affected-file reruns: 96/96

The PR intentionally remains Draft. I will wait for hosted CI, then perform thread-by-thread readback before resolving comments or marking it Ready for review.

@luvs01

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Review-thread readback against the public head 8790758 is complete. I resolved 26 of 27 threads whose fixes and regression coverage are present in this revision. I intentionally left only the hosted keyring runner-isolation thread open until the Ubuntu, Windows, and macOS keyring jobs actually run on this head. The PR remains Draft while that hosted evidence and the latest dev refresh are pending.

@luvs01
luvs01 force-pushed the feat/656-native-main-profiles branch from 8790758 to 9273337 Compare August 3, 2026 06:38
@luvs01

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Draft checkpoint updated onto the latest dev baseline.

  • base: 56b4181876dfb6d5bdcf4928398ec61aa3bda223
  • head: 927333765cf4bfa43b8b8fff9559a04975099b29
  • all 59 commits remained patch-equivalent in git range-diff (59/59 =)
  • no text conflicts; the upstream hosted-tool config changes and this PR's atomic-write crash seam remain intact
  • focused config/management/keyring/native-profile/CI/lifetime matrix: 247 passed, 0 failed
  • bun run typecheck, bun run privacy:scan, and git diff --check: passed

The PR remains Draft. The hosted keyring-smoke review thread stays open until the disposable Ubuntu, Windows, and macOS jobs have actually run.

@luvs01

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

@Wibias The latest Draft head is ready for a Ready-state decision:

  • head 927333765cf4bfa43b8b8fff9559a04975099b29 on dev 56b4181876dfb6d5bdcf4928398ec61aa3bda223
  • all 59 commits remained patch-equivalent in git range-diff
  • focused config/management/keyring/native-profile/CI/lifetime matrix: 247 passed, 0 failed
  • typecheck, privacy scan, and diff check passed
  • all prior code-review requirements are implemented; the hosted keyring-smoke thread remains open pending actual disposable Ubuntu/Windows/macOS runs
  • the PR workflows currently show action_required before starting jobs because this is a fork PR

Would you recommend marking this PR Ready now so review and hosted workflow approval can proceed, or keeping it Draft until workflow sponsorship is available?

@Wibias
Wibias marked this pull request as ready for review August 3, 2026 07:41

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 927333765c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/codex/native-profile-processes.ts Outdated
Comment thread src/server/management/system-restart.ts Outdated
Comment thread src/codex/native-profile-manager.ts Outdated
luvs01 added 3 commits August 3, 2026 22:38
# Conflicts:
#	src/codex/auth-context.ts
#	src/providers/openai-sidecar.ts
#	src/server/responses/compact.ts
#	src/server/responses/core.ts
#	src/server/search.ts
#	tests/responses-compaction-routing.test.ts

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Current revision checkpoint: 5a7d53ab57b40be485f6d1b3df5fa93718a7e0df on dev 14b20def27f2d45f929c0dbb853fd0993ca61663.

Completed review follow-up:

  • Unix interpreter-launched Codex detection is implemented and covered.
  • Restart handoff now separates listener stop from lifecycle release and has bounded late-settlement behavior.
  • POSIX SQLite lock ownership uses a retained ref-counted stable descriptor.
  • Exact-account routing from current dev is integrated with native-main turn admission, including compact, sidecar, search, and Responses paths.
  • The three corresponding current-review P1 threads have been answered and resolved.
  • The hosted keyring-isolation thread remains open until the disposable hosted jobs actually run.

Validation on official stable Bun 1.3.14+0d9b296af:

  • Focused native-profile/routing/restart/CI matrix: 659 pass / 0 fail / 2 Darwin-only skips.
  • Typecheck, privacy scan, and diff check: passed.
  • Full root suite completed with raw 7,780 pass / 6 skip / 11 fail / 6 errors after 1,682 seconds.
  • Canonical isolated reruns cleared 9 of the 11 failed paths: the Bun-runtime environment override, the Claude endpoint/socket case, and all six GUI dependency-load errors.
  • The two remaining isolated failures are unchanged dev Windows test-hermeticity issues: cli-restore-back discovers the real port-10100 proxy, and codex-v2-gate expects POSIX raw args while the same file requires the Windows codex.cmd wrapper. No feat(codex): add encrypted native main profiles #863 production code was changed to conceal those baseline failures.

Hosted status:

  • Cross-platform CI, Service lifecycle, and React Doctor are currently action_required; no hosted job result exists yet for this head.

@Wibias The PR remains Ready on this current revision. Please re-review when the workflow approval and hosted evidence are available.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

171-183: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

gh run list does not accept jq's --arg, so this substitution likely yields an empty ci_url and permanently blocks every release.

Lines 175-182 invoke:

gh run list ... --jq --arg branch "${GITHUB_REF#refs/heads/}" 'map(select(...)) | .[0].url // ""'

--arg belongs to the jq binary. The GitHub CLI exposes --jq <expression> as a single-value flag and does not forward jq variable bindings. Two failure modes follow, and both are bad:

  1. gh rejects --arg as an unknown flag. The command substitution at line 171 returns an empty string, line 185 sees -z "$ci_url", and the step exits 1 with the "No successful Cross-platform CI run found" error at line 192.
  2. gh accepts the flag order and --jq consumes the literal string --arg as its filter expression. branch, the branch name, and the real filter then arrive as stray positional arguments, and the jq program is invalid.

In both cases ci_url is empty on every run. The gate fails closed, so there is no security exposure — but the release workflow can never reach the publish step. The referenced $branch variable at line 182 would also be undefined inside the filter.

Bind the branch inside the filter with env instead, which needs no variable binding.

🔧 Proposed fix using an environment lookup inside the jq filter
           ci_url="$(
-            # shellcheck disable=SC2016
-            # `$branch` is a jq variable bound by --arg, not a shell variable, so
-            # the filter must stay single-quoted.
-            gh run list \
+            # shellcheck disable=SC2016
+            # `env.CI_BRANCH` is resolved by jq from the process environment, not
+            # by the shell, so the filter must stay single-quoted.
+            CI_BRANCH="${GITHUB_REF#refs/heads/}" gh run list \
               --workflow ci.yml \
               --commit "$GITHUB_SHA" \
               --status success \
               --limit 10 \
               --json conclusion,headSha,url,workflowName,event,headBranch \
-              --jq --arg branch "${GITHUB_REF#refs/heads/}" \
-                'map(select(.event == "push" and .headBranch == $branch)) | .[0].url // ""'
+              --jq 'map(select(.event == "push" and .headBranch == env.CI_BRANCH)) | .[0].url // ""'
           )"

Run the following script to confirm the flag surface of the installed gh release:

#!/bin/bash
# Description: Check whether `gh run list` accepts jq's --arg flag.
set -euo pipefail

gh --version

echo "=== documented flags for 'gh run list' ==="
gh run list --help 2>&1 || true

echo "=== does --arg parse at all? (expect an unknown-flag error) ==="
gh run list --limit 1 --json url --jq --arg branch main 'map(.url)' 2>&1 || echo "exit=$?"

echo "=== control: plain --jq with an env lookup ==="
CI_BRANCH=main gh run list --limit 1 --json url,headBranch \
  --jq 'map(select(.headBranch == env.CI_BRANCH)) | .[0].url // ""' 2>&1 || echo "exit=$?"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 171 - 183, Update the ci_url
command substitution’s gh run list invocation to remove the unsupported --arg
branch arguments and expose the branch through an environment variable. Change
the --jq filter to compare headBranch with that environment value via jq’s env
lookup, preserving the existing push-event filtering and empty-string fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 485-497: Extract the duplicated Linux keyring isolation and unlock
logic into one committed helper script, preserving its temporary directories,
cleanup trap, permissions, isolated environment, dbus-run-session, and fail-fast
bash settings. Update .github/workflows/ci.yml lines 485-497 to invoke the
helper, and update .github/workflows/release.yml lines 108-125 to invoke the
same helper instead of duplicating the block; alternatively, remove the
redundant release step while retaining the CI gate.

In @.github/workflows/release.yml:
- Around line 95-96: The audit command is duplicated and omits the GUI
dependency tree. Update package.json’s audit:high script to audit both the root
and gui trees, replace the inline command in .github/workflows/release.yml lines
95-96 with bun run audit:high, and update scripts/release.ts lines 301-302 to
invoke the same script via runLoud; these three locations must share the single
definition.

In `@docs-site/src/content/docs/ja/reference/cli/providers-accounts.md`:
- Around line 167-186: Synchronize the operational guidance from the English
providers-accounts reference into the listed locale pages:
docs-site/src/content/docs/ja/reference/cli/providers-accounts.md (lines
167-186), docs-site/src/content/docs/ko/reference/cli/providers-accounts.md
(lines 167-186),
docs-site/src/content/docs/ru/reference/cli/providers-accounts.md (lines
218-237), and
docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md (lines
201-219). Add translated guidance covering canonical vault placement, the safe
cleanup boundary for crash residue, and preview-layout migration, ensuring no
locale contradicts the English source.

In `@scripts/keyring-smoke.ts`:
- Around line 39-41: Zero the keyring-returned readback buffer after comparing
or copying it, alongside the existing cleanup of stored and secret in the
smoke-test flow. Update the handling around entry.getSecret and the cleanup
block so every plaintext buffer, including readback, is wiped without changing
the validation behavior.
- Around line 66-71: Update the Object.defineProperty call in the cleanupFailed
handling of the keyring smoke operation to set enumerable: true for the attached
cleanupError property, preserving the existing configurable and value behavior
so compound failures include cleanup details in diagnostics.

In `@src/cli/account-main.ts`:
- Around line 116-137: Update maintainStageLease around the apiJson heartbeat
request to enforce a deadline before leaseExpiresAt with a safety margin, using
an abort mechanism that cancels a request that never settles. When the deadline
expires, terminate child and return the lost result so the existing
stage-cancellation flow runs; add coverage with a never-settling fetchImpl
verifying child termination and cancellation.

---

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 171-183: Update the ci_url command substitution’s gh run list
invocation to remove the unsupported --arg branch arguments and expose the
branch through an environment variable. Change the --jq filter to compare
headBranch with that environment value via jq’s env lookup, preserving the
existing push-event filtering and empty-string fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4d8828a1-5008-405a-817f-8925b31a7c49

📥 Commits

Reviewing files that changed from the base of the PR and between fc1f6ab and 5a7d53a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • devlog/_plan/260801_native_main_profiles/000_design.md
  • devlog/_plan/260801_native_main_profiles/001_validation.md
  • devlog/_plan/260801_native_main_profiles/002_implementation.md
  • docs-site/src/content/docs/ja/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/ko/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/ru/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md
  • package.json
  • scripts/keyring-smoke.ts
  • scripts/release.ts
  • src/cli/account-api.ts
  • src/cli/account-main.ts

Comment thread .github/workflows/ci.yml
Comment thread .github/workflows/release.yml Outdated
Comment thread docs-site/src/content/docs/ja/reference/cli/providers-accounts.md
Comment thread scripts/keyring-smoke.ts Outdated
Comment thread scripts/keyring-smoke.ts
Comment thread src/cli/account-main.ts
@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

[GD] Verdict: changes-requested

PR: #863 — feat(codex): add encrypted native main profiles
Head: 42d63ef6 on dev (mergeable: MERGEABLE, not draft)

Semantic propagation

  • Concepts audited: home binding (shared metadata under canonical CODEX_HOME vs instance-local OPENCODEX_HOME staging), credential-store modes (legacy auth.json / encrypted vault / OS keyring), vault/journal/quarantine format, recovery decision model, staging leases, management boundary, __main__ runtime reconciliation, routing/drain admission fences, process detection, keyring CI isolation.
  • Authoritative sources: src/codex/native-profile-manager.ts, native-main-lock-file.ts, native-profile-processes.ts, recovery/journal modules, auth-context.ts, src/server/management/*, src/server/claude-messages.ts.
  • Producers/consumers checked: manager ↔ stage store ↔ recovery ↔ startup gate ↔ CLI (account-main.ts) ↔ management API ↔ CI smoke; rotation/quota/prime paths; request logs.
  • Public/derived representations checked: CLI help/usage, docs-site en + ja/ko/ru/zh-cn, API DTOs, request logs, privacy:scan.
  • Variant partitions checked: POSIX vs Windows locking; file vs keyring stores; main vs pool routing; direct vs routed/native routes; supervised vs detached restart; legacy vs preview layout.
  • Positive/negative assertions: process-probe interpreter contract, same-process lock regression, cross-process exclusion, crash matrix, startup gates, route admission tests.
  • Representation mismatches: none — the newest en operational guidance (canonical vault dir, preview migration) is now synced into ja/ko/ru/zh-cn.
  • Variant coverage gaps: Unix probe still misses a directly-executed platform-suffixed native binary (codex-x86_64-unknown-linux-gnu style); narrow edge, non-blocking.
  • Axis verdict: pass with the noted edge.

Usefulness

Real, spec-backed capability for issue #656: encrypted native main profiles, OS-keyring key custody, transactional switch with crash recovery, admission fencing, and the maintainer-requested hardening (staging cleanup/expiry, SQLite claim + stable lock verification, startup journal gates, crash matrix, route auth tests, release audit + keyring smoke). Dashboard UI is deferred per the design doc. Useful.

Bugs / correctness

  • Method: bug-review + complementary pass (Codex host; Bugbot n/a). All findings from this run are fixed with regression coverage:
    • same-process POSIX lock release → shared-fd registry (native-main-lock-file.ts) + dedicated test;
    • Unix interpreter-launch probe gap → interpreter/entrypoint contract tests;
    • deadline restart handoff race → listener-stop before spawn, bounded replacement-readiness budget, child-exit rejection (41 restart/drain tests);
    • staging heartbeat stall (account-main.ts) → deadline-raced heartbeat with 30s safety margin, child kill + lost-lease return, deterministic clock tests.
  • Local tip: typecheck clean; rotation 22/22, storage 2/2, manager 49/49, processes 9/9, restart+drain 41/41, startup 12/12. Box-only env failures (symlink EPERM, icacls/EBUSY) are not product defects.

Security

  • Crypto sound (AES-256-GCM, random 12B nonce, AAD binds format/home/profile/identity/digest); no token leakage in API/CLI/docs; management routes behind real auth/CSRF/origin tests; staging plaintext scrubbed fail-closed; pinned action SHAs, persist-credentials: false, exact @napi-rs/keyring@1.3.0.
  • keyring-smoke minors fixed (readback buffer zeroed, cleanupError enumerable).
  • Old-head keyring ubuntu CI failure fixed by the --unlock isolation flow in both workflows; green on the previous head.

Spec / standards

  • Spec: issue #656; design docs under devlog/_plan/260801_native_main_profiles/* follow the in-repo devlog convention. Locale sync complete. No gaps.

Reviews

  • Maintainers: none open; all chatgpt-codex-connector threads resolved.
  • CodeRabbit: 2 of 36 threads remain, both decline-with-rationale: Vtkmy (keyring isolation — the keyring-smoke matrix runs only on hosted ephemeral runners) and WDSG- (duplicated isolation block — identical fix applied in both workflows; extraction optional).

Base / CI

  • Conflicts: clean — head merged current dev (42d63ef6), mergeable: MERGEABLE.
  • Required checks on head: Cross-platform CI, React Doctor, and Service lifecycle are action_required on 42d63ef (fork-run approval queue) — not yet green on this exact head. The previous head 5a7d53ab was fully green (Cross-platform CI run 30832507034, including keyring ubuntu/windows/macos).

Gate

Not draft. Only remaining blocker: approve and confirm required CI green on 42d63ef, then close (or reply-decline) the two trivial CodeRabbit threads.

Bottom line

All substantive findings from this review are fixed on the current head with regression tests, and the base conflict is resolved. This is merge-ready modulo the operational gate: a green required-CI run on 42d63ef (currently queued for approval) and closing the two trivial CodeRabbit threads. Verdict: changes-requested.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
src/providers/quota.ts (2)

1110-1121: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Deduplicate asynchronous pool-key computation

cacheKeyWithAggregationState awaits listCodexAuthAccountsSnapshot before inflight admission. Concurrent callers can receive different snapshots because fetchMainAccountInfoAttempt returns an empty snapshot while another caller holds the native-main claim. They then derive different codex-pool keys and run separate provider probes. Compute the aggregation key inside a shared synchronous-key flight, or add single-flight handling for the snapshot. Moving inflight.set alone does not fix this.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/quota.ts` around lines 1110 - 1121, Deduplicate the
asynchronous snapshot/key computation before provider probing: update the flow
around cacheKeyWithAggregationState and inflight so concurrent callers share one
in-progress aggregation-key operation, rather than deriving keys from
inconsistent listCodexAuthAccountsSnapshot results. Ensure callers converge on
the same codex-pool key and existing invalidation/forceRefresh semantics remain
intact; moving inflight admission without sharing snapshot computation is
insufficient.

100-150: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

cacheKeyWithAggregationState is correctly privacy-safe; one small durability note on the sort.

The comment on line 124 and the body of quotaSignatureValue agree: only percentages, reset timestamps, and boolean state enter the digest. Account ids and email addresses stay out of the cache key. hasCodexPoolProvider also correctly excludes direct mode, which matches the direct-mode early return in fetchChatGptForwardQuota at line 268.

One note on line 120: localeCompare is locale-sensitive. The digest is only ever compared against another digest computed in the same process, so ordering instability across locales cannot change behavior today. If this signature is ever persisted or compared across processes, switch to a plain codepoint comparison so the digest becomes reproducible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/providers/quota.ts` around lines 100 - 150, Update quotaSignatureValue’s
customWindows sorting to use a deterministic codepoint-based comparison instead
of localeCompare, ensuring persisted or cross-process digests have stable
ordering. Preserve the existing label-based sort and all other signature fields
unchanged.
tests/provider-quota.test.ts (1)

1013-1021: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Line 1019 is a reference-identity check that passes on the buggy path too; only line 1020 carries signal.

Trace what expect(next).not.toBe(racedResponse) actually proves. Suppose the racing probe wrongly committed its pre-change result to the cache. The later non-forced call on line 1018 would then take the cacheFresh fast path and return cache!.response. That object was constructed at src/providers/quota.ts line 1171 as { ...response, reports }, which is a different object identity from racedResponse. So not.toBe passes in both the correct and the incorrect case. The assertion cannot fail for the reason the test name states.

Line 1020 is the assertion that does the work: it requires currentAccount to reflect activeCodexAccountId = "added", which a wrongly committed pre-change cache entry would not produce.

Two improvements:

  1. Replace line 1019 with an assertion that the racing probe did not publish. Call fetchProviderQuotaReports(config) twice after the race and assert that the second call returns the same object as the first. That proves next performed the commit, and therefore that the racing probe did not.
  2. Use the providerQuotaBeforePublishForTests hook that this PR added. The current gating relies on the mocked fetch resolving before the publish step, so the ordering between the activeCodexAccountId flip and the commit-key recheck at src/providers/quota.ts line 1136 is implicit. The hook makes that ordering explicit and removes the dependency on fetch-call sequencing.
🔧 Proposed fix: assert the commit owner instead of object identity
     const racing = fetchProviderQuotaReports(config, true);
     await started;
     config.activeCodexAccountId = "added";
     release();
     const racedResponse = await racing;
     const next = await fetchProviderQuotaReports(config);
-    expect(next).not.toBe(racedResponse);
     expect(next.reports[0]?.aggregation?.currentAccount).toMatchObject({ plan: "prolite", quota: { weeklyPercent: 77 } });
+    // The racing probe must not have published; `next` is the probe that committed.
+    expect(next).not.toBe(racedResponse);
+    expect(await fetchProviderQuotaReports(config)).toBe(next);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/provider-quota.test.ts` around lines 1013 - 1021, Strengthen the race
test around fetchProviderQuotaReports: use the
providerQuotaBeforePublishForTests hook to explicitly pause the racing request
before its publish/commit-key recheck, then change the post-race assertions so
the first non-forced call commits the result and a second non-forced call
returns that same response object. Preserve the currentAccount assertion to
verify the updated account is reflected, and remove the ineffective direct
comparison with racedResponse.
src/codex/auth-api.ts (1)

1009-1014: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Keep the main-account quota values and updatedAt from one snapshot. MainAccountInfo.quota explicitly omits updatedAt (src/codex/main-account-cache.ts:6-10), so do not access mainInfo.quota.updatedAt. parseUsageQuota accepts reset-credit-only responses, while setAccountQuotaFromParsed can advance updatedAt and preserve older usage values (src/codex/quota.ts:134-179, 360-367). Return and cache one stored quota snapshot, or invalidate the cached quota when the store write does not commit. This prevents aggregateCodexPoolCapacity from applying freshness checks to a different quota vintage (src/providers/codex-capacity.ts:185-203).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/codex/auth-api.ts` around lines 1009 - 1014, Update the main-account
quota flow around quotaForPlan to use one stored quota snapshot for both
returned values and updatedAt, rather than combining mainInfo.quota with a
separate getAccountQuota result. Reuse the snapshot produced after
setAccountQuotaFromParsed, including preserved usage and reset-credit-only
updates, and invalidate the cached quota when the store write does not commit so
aggregateCodexPoolCapacity never sees mixed quota vintages.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/providers/quota.ts`:
- Around line 1107-1109: Update src/providers/quota.ts lines 1107-1109 in the
prefetchedCodexSnapshot initialization to prefetch whenever
hasCodexPoolProvider(config) is true, passing forceRefresh as the probe
argument. Add a sibling regression test in tests/provider-quota.test.ts lines
524-545 that calls fetchProviderQuotaReports(config) without forceRefresh and
verifies the calls map records one probe per account.

In `@tests/keyring-smoke.test.ts`:
- Around line 58-70: Extend the “zeros the buffer returned by the keyring” test
to capture the Buffer created by createRandomBytes, then assert that this
generated secret is also zeroed after runKeyringSmoke completes. Preserve the
existing readback assertion and ensure the captured secret is distinct from the
keyring-returned buffer.
- Around line 90-91: Update the assertion in the error-handling test near the
existing Object.keys(error) check to inspect the nested cleanupError directly:
assert cleanupError.message contains “could not delete the temporary entry”
instead of relying on Bun.inspect(error), while preserving the enumerability
assertion unchanged.

---

Outside diff comments:
In `@src/codex/auth-api.ts`:
- Around line 1009-1014: Update the main-account quota flow around quotaForPlan
to use one stored quota snapshot for both returned values and updatedAt, rather
than combining mainInfo.quota with a separate getAccountQuota result. Reuse the
snapshot produced after setAccountQuotaFromParsed, including preserved usage and
reset-credit-only updates, and invalidate the cached quota when the store write
does not commit so aggregateCodexPoolCapacity never sees mixed quota vintages.

In `@src/providers/quota.ts`:
- Around line 1110-1121: Deduplicate the asynchronous snapshot/key computation
before provider probing: update the flow around cacheKeyWithAggregationState and
inflight so concurrent callers share one in-progress aggregation-key operation,
rather than deriving keys from inconsistent listCodexAuthAccountsSnapshot
results. Ensure callers converge on the same codex-pool key and existing
invalidation/forceRefresh semantics remain intact; moving inflight admission
without sharing snapshot computation is insufficient.
- Around line 100-150: Update quotaSignatureValue’s customWindows sorting to use
a deterministic codepoint-based comparison instead of localeCompare, ensuring
persisted or cross-process digests have stable ordering. Preserve the existing
label-based sort and all other signature fields unchanged.

In `@tests/provider-quota.test.ts`:
- Around line 1013-1021: Strengthen the race test around
fetchProviderQuotaReports: use the providerQuotaBeforePublishForTests hook to
explicitly pause the racing request before its publish/commit-key recheck, then
change the post-race assertions so the first non-forced call commits the result
and a second non-forced call returns that same response object. Preserve the
currentAccount assertion to verify the updated account is reflected, and remove
the ineffective direct comparison with racedResponse.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 11d65cd4-f560-49c2-8646-db01a1db3dca

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7d53a and 42d63ef.

⛔ Files ignored due to path filters (1)
  • gui/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • docs-site/src/content/docs/ja/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/ko/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/ru/reference/cli/providers-accounts.md
  • docs-site/src/content/docs/zh-cn/reference/cli/providers-accounts.md
  • gui/package.json
  • package.json
  • scripts/keyring-smoke.ts
  • scripts/release.ts
  • src/cli/account-api.ts
  • src/cli/account-main.ts
  • src/codex/auth-api.ts
  • src/providers/quota.ts
  • tests/ci-workflows.test.ts
  • tests/cli-native-profile.test.ts
  • tests/keyring-smoke.test.ts
  • tests/provider-quota.test.ts
  • tests/release-helper.test.ts

Comment thread src/providers/quota.ts Outdated
Comment thread tests/keyring-smoke.test.ts
Comment thread tests/keyring-smoke.test.ts Outdated

luvs01 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor Author

Current revision checkpoint: 2736ae11419aa05cb488a5c8b6efbd6c0b0f26ae

  • Integrated current dev at 959e39f470f2c5fe39b212b889577be1b62857cc, including feat(gui): show weighted Codex pool capacity and next recovery #878. The semantic conflict resolution preserves both native-main identity-generation fencing and weighted capacity/cache-signature behavior.
  • Addressed the earlier review follow-ups: hosted-only keyring matrix, non-duplicated release gate, root+GUI high audit, locale guidance, keyring buffer hygiene and compound diagnostics, and a bounded staging-heartbeat deadline.
  • Addressed the latest review pass: non-forced Pool key/report phases now share one snapshot before the intentional commit-key recheck; tests also assert generated-secret zeroization and inspect the nested cleanup error directly.
  • GUI security overrides are brace-expansion@5.0.9 and postcss@8.5.18.

Validation on official Bun 1.3.14+0d9b296af:

  • Current-head provider/account/capacity/keyring matrix: 66 passed, 0 failed, 240 assertions.
  • Current-head typecheck, privacy scan, and diff check passed.
  • Merged-head GUI validation before the latest root-only follow-up: 583 passed, 0 failed, 2,708 assertions; lint and production build passed. The latest commit has no GUI changes.
  • Root + GUI high-severity audit: no vulnerabilities.
  • Frozen GUI install: 161 installs / 193 packages, no lock changes.
  • As of this readback, unresolved review threads: 0.

GitHub Actions on this exact head: React Doctor, Service lifecycle, and Cross-platform CI all completed successfully. The PR remains Ready and mergeable; requested human reviews are still pending.

@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

[GD] Verdict: changes-requested

PR: #863 — feat(codex): add encrypted native main profiles
Head: 42d63ef6 on dev (mergeable: MERGEABLE, mergeStateStatus: CLEAN, not draft)

Semantic propagation

  • Concepts audited: home binding (shared metadata under canonical CODEX_HOME vs instance-local OPENCODEX_HOME staging), credential-store modes (legacy auth.json / encrypted vault / OS keyring), vault/journal/quarantine format, recovery decision model, staging leases, management boundary, __main__ runtime reconciliation, routing/drain admission fences, process detection, keyring CI isolation.
  • Authoritative sources: src/codex/native-profile-manager.ts, native-main-lock-file.ts, native-profile-processes.ts, recovery/journal modules, auth-context.ts, src/server/management/*, src/server/claude-messages.ts.
  • Derived/public representations checked: CLI help/usage, docs-site en + ja/ko/ru/zh-cn (sync confirmed), API DTOs, request logs, privacy:scan.
  • Variant partitions checked: POSIX vs Windows locking; file vs keyring stores; main vs pool routing; direct vs routed/native routes; supervised vs detached restart; legacy vs preview layout.
  • Representation mismatches: none. Residual edge: Unix probe still misses a directly-executed platform-suffixed native binary (codex-x86_64-unknown-linux-gnu style) — narrow, non-blocking.
  • Axis verdict: pass with the noted edge.

Usefulness

Real, spec-backed capability for issue #656: encrypted native main profiles, OS-keyring key custody, transactional switch with crash recovery, admission fencing, and the maintainer-requested hardening. Dashboard UI deferred per design doc. Useful.

Bugs / correctness

All findings from the prior verdict round are fixed with regression coverage: same-process POSIX lock release (shared-fd registry + dedicated test), Unix interpreter-launch probe gap (contract tests), deadline restart handoff race (listener-stop + readiness budget + child-exit rejection), and the staging heartbeat stall (deadline-raced heartbeat, 30s safety margin, deterministic clock tests).

New this round — CodeRabbit threads, verified against the code at 42d63ef:

  1. src/providers/quota.ts:1109 (Major, valid) — on the non-forced pool path the snapshot prefetch is gated on forceRefresh, so the pool is walked twice (cacheKeyWithAggregationState at line 134 and fetchChatGptForwardQuota at line 277 both fall back to listCodexAuthAccountsSnapshot), and the cache key can describe a different snapshot than the published report; no test covers it. Quick win: prefetch whenever hasCodexPoolProvider(config) and pass forceRefresh into the probe; add a sibling test near provider-quota.test.ts:524.
  2. tests/keyring-smoke.test.ts:70 (trivial, valid) — happy path should also assert the generated secret buffer is zeroed (the error path already does).
  3. tests/keyring-smoke.test.ts:91 (trivial, valid) — assert cleanupError.message directly instead of relying on Bun.inspect(error) formatting; keep the enumerability check.

Local tip: core suites verified on prior heads and the fix commits; the full suite is covered by CI (green).

Security

Crypto and credential handling remain sound (AES-256-GCM, AAD binding, OS keyring custody, fail-closed staging cleanup, management auth/CSRF/origin). Prior keyring-smoke minors (readback zeroization, enumerable cleanupError) are fixed. No open security findings beyond the two trivial test assertions above.

Spec / standards

Spec: issue #656; design docs under devlog/_plan/260801_native_main_profiles/*. Locale sync complete (ja/ko/ru/zh-cn). No gaps.

Reviews

  • Maintainers: none open; all prior connector threads resolved.
  • CodeRabbit: 3 of 39 threads open — the quota.ts double-probe (above, should be fixed or declined with rationale) and the two trivial keyring-smoke assertions.

Base / CI

  • Base: clean — head merged current dev (959e39f4, incl. feat(gui): show weighted Codex pool capacity and next recovery #878); mergeable: MERGEABLE, mergeStateStatus: CLEAN.
  • Required CI: green on 42d63ef — Cross-platform CI succeeded (incl. keyring ubuntu/windows/macos); React Doctor, Service lifecycle, gates, enforce-target all success.

Gate

Not draft. Remaining before merge-ready: address or decline the quota.ts double-probe thread, apply the two trivial keyring-smoke test assertions, and close the corresponding CodeRabbit threads.

Bottom line

All substantive review findings are fixed, the base is clean, and required CI is green on 42d63ef. The remaining delta is small: one quick-win performance/consistency fix in the dev-merged quota code, two one-line test assertions, and closing those threads. Verdict: changes-requested (narrow).

@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

[GD] Addressed feedback

feedbacks:

  • issue_comment:5154142332

commit: 2736ae1

@Wibias

Wibias commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Thanks @luvs01 — merging this.

Why it helps: this adds encrypted native-main profiles with OS-keyring key custody, transactional switching with crash recovery, and admission fencing for issue #656; every finding from the review loop (POSIX lock ownership, process probe, restart handoff, staging heartbeat, keyring CI) is fixed with regression coverage on 2736ae11.

Ship it.

@Wibias
Wibias merged commit 1d1c803 into lidge-jun:dev Aug 3, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants